#include<mpi.h>
#include<stdio.h>

#define MAX 50000
#define COEFFICIENT 1

double power(double x, int degree)
{     
      if(degree == 0)  return 1;
      
      if(degree == 1)  return x;

      return x * power(x, degree - 1);
}

double sequential(int coeffArr[], double x)
{
   int maxDegree = MAX - 1;
   int i;
   double  answer = 0;
   
   for( i = 0; i < maxDegree;  i++)
   {
      
      double powerX = power(x, i);

      //printf("%f ", powerX);
      answer = answer + coeffArr[i] * powerX;
   }
   return answer;
}

double fixedChunks(int coeffArr[], double x)
{
   int rank, numProcs;
   MPI_Comm_rank(MPI_COMM_WORLD, &rank);
   MPI_Comm_size(MPI_COMM_WORLD, &numProcs);
   
   int i;
   double answer = 0;
   int maxDegree = MAX - 1;
   
   int startIndex = (rank * MAX)/numProcs;
   int endIndex = (rank + 1) * MAX/numProcs;

   if(rank == numProcs - 1)
     endIndex = MAX;
   
   for( i = startIndex; i < endIndex; i++)
   {
      double powerX = power(x, i);

      answer = answer + coeffArr[i] * powerX;
   }
   return answer;
}

double roundRobin(int coeffArr[], double x)
{
   int rank, numProcs;
   MPI_Comm_rank(MPI_COMM_WORLD, &rank);
   MPI_Comm_size(MPI_COMM_WORLD, &numProcs);
   
   int i;
   double answer = 0;
   int maxDegree = MAX - 1;
   
   for( i = rank; i < maxDegree; i = i+numProcs)
   {
      double powerX = power(x, i);

      answer = answer + coeffArr[i] * powerX;
   }
   return answer;
}

void initialize(int coeffArr[])
{
   int maxDegree = MAX - 1;
   int i;
   for( i = 0; i < maxDegree; i++)
   {
      coeffArr[i] = COEFFICIENT;
   }
}

int main(int argc, char **argv)
{
 int coeffArr[MAX]; 
 
 initialize(coeffArr);
 double x = 0.99;
 
 int rank;
 MPI_Init(&argc, &argv);
 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
 
 double answerSequential;
 double startTimer, endTimer;
 if(rank == 0)
 {
   startTimer = MPI_Wtime();
  
   answerSequential = sequential(coeffArr, x);
 
   endTimer = MPI_Wtime();
 
   printf("Answer Sequential %f and Time taken is %f\n", answerSequential, (endTimer - startTimer));
 }
 
 MPI_Barrier(MPI_COMM_WORLD);
 
 /////////////////////////////////////////////////////////////////////////////////

 startTimer = MPI_Wtime();

 double answerRoundRobin = roundRobin(coeffArr, x);

 endTimer = MPI_Wtime();

 double finalAnswerRR;
 
 printf("Rank = %d Answer Round Robin %f, Time = %f\n", rank, answerRoundRobin, (endTimer - startTimer));
 fflush(stdout);
 
 MPI_Reduce(&answerRoundRobin, &finalAnswerRR, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);

 
 /////////////////////////////////////////////////////////////////////////////////////////
 printf("\n");
 fflush(stdout);
 
 MPI_Barrier(MPI_COMM_WORLD); 
 
 startTimer = MPI_Wtime();
 
 double answerFixedChunks = fixedChunks(coeffArr, x);

 endTimer = MPI_Wtime();

 printf("Rank = %d Answer Fixed Chunks %f, Time = %f\n", rank, answerFixedChunks, (endTimer - startTimer));
 fflush(stdout);
 
 double finalAnswerFC;
 MPI_Reduce(&answerFixedChunks, &finalAnswerFC, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
 
 printf("\n");
 fflush(stdout);
 
 if(rank == 0)
  printf("Answer verification roundRobin = %f fixedChunks = %f \n", finalAnswerRR, finalAnswerFC);
 
 MPI_Finalize();
 return 0; 
}